Imports Microsoft.VisualBasic
Imports System
Imports System.Web
Imports System.Collections.Specialized
Imports System.Configuration
Imports System.Web.Configuration
Imports System.Collections.Generic
Imports System.Configuration.Provider
Imports System.Security.Permissions
Imports System.Data.Common
Imports System.Data

Namespace CalibreVB

    Public Class DataTableSitemapProvider
        Inherits StaticSiteMapProvider

        Protected Const _errmsg1 As String = "Missing node ID"
        Protected Const _errmsg2 As String = "Duplicate node ID"
        Protected Const _errmsg3 As String = "Missing parent ID"
        Protected Const _errmsg4 As String = "Invalid parent ID"
        Private _name As String
        Protected _indexID As Integer, _indexTitle As Integer, _indexUrl As Integer, _indexDesc As Integer, _indexRoles As Integer, _indexParent As Integer
        Protected _nodes As New Dictionary(Of Integer, SiteMapNode)(16)
        Protected ReadOnly _lock As New Object()
        Protected _root As SiteMapNode
        Protected dt As DataTable

        Public Overloads Overrides Sub Initialize(ByVal name As String, ByVal config As NameValueCollection)
            ' Verify that config isn't null
            If config Is Nothing Then
                Throw New ArgumentNullException("config")
            End If

            ' Call the base class's Initialize method
            MyBase.Initialize(name, config)

            ' SiteMapProvider processes the securityTrimmingEnabled
            ' attribute but fails to remove it. Remove it now so we can
            ' check for unrecognized configuration attributes.

            If config("securityTrimmingEnabled") IsNot Nothing Then
                config.Remove("securityTrimmingEnabled")
            End If

            ' Throw an exception if unrecognized attributes remain
            If config.Count > 0 Then
                Dim attr As String = config.GetKey(0)
                If Not String.IsNullOrEmpty(attr) Then
                    'Throw New ProviderException("Unrecognized attribute: " + attr)
                End If
            End If
            _name = name
            'HttpContext.Current.Response.Write(_name)

        End Sub

        ''' Returns the root node of our sitemap to be digested by the appropriate control
        Protected Overrides Function GetRootNodeCore() As SiteMapNode
            '_root = Nothing
            Return BuildSiteMap()
        End Function

        ''' Creates a sitemap node from a datarow
        Private Function CreateSiteMapNodeFromDataRow(ByVal dr As DataRow) As SiteMapNode
            ' Make sure the node ID is present
            If dr(_indexID) Is DBNull.Value Then
                Throw New ProviderException(_errmsg1)
            End If

            ' Get the node ID from the DataReader
            Dim id As Integer = dr(_indexID)

            ' Make sure the node ID is unique
            If _nodes.ContainsKey(id) Then
                'Throw New ProviderException(_errmsg2)
            End If

            ' Get title, URL, description, and roles from the DataReader
            Dim title As String = IIf(dr(_indexTitle) Is DBNull.Value, Nothing, dr(_indexTitle).ToString().Trim())
            Dim url As String = IIf(dr(_indexUrl) Is DBNull.Value, Nothing, dr(_indexUrl).ToString().Trim())
            Dim description As String = IIf(dr(_indexDesc) Is DBNull.Value, Nothing, dr(_indexDesc).ToString().Trim())
            'Dim roles As String = IIf(reader.IsDBNull(_indexRoles), Nothing, reader.GetString(_indexRoles).Trim())
            'We won't be using roles
            Dim roles As String = "*"

            ' If roles were specified, turn the list into a string array
            Dim rolelist As String() = Nothing
            If Not String.IsNullOrEmpty(roles) Then
                rolelist = roles.Split(New Char() {","c, ";"c}, 512)
            End If

            ' Create a SiteMapNode
            Dim node As New SiteMapNode(Me, id.ToString(), url, title, description, rolelist, Nothing, Nothing, Nothing)

            ' Record the node in the _nodes dictionary
            _nodes.Add(id, node)

            ' Return the node        
            Return node
        End Function

        ''' Returns the parent node of the node described in a data row
        Private Function GetParentNodeFromDataRow(ByVal dr As DataRow) As SiteMapNode
            ' Make sure the parent ID is present
            If dr("Parent") Is System.DBNull.Value Then
                Throw New ProviderException(_errmsg3)
            End If

            ' Get the parent ID from the DataReader
            Dim pid As Integer = CInt(dr("Parent"))

            ' Make sure the parent ID is valid
            If Not _nodes.ContainsKey(pid) Then
                Throw New ProviderException(_errmsg4)
            End If

            ' Return the parent SiteMapNode
            Return _nodes(pid)
        End Function

        ''' This resets the SiteMapProvider after an update has been made
        Public Sub OnSiteMapChanged()
            SyncLock _lock
                Clear()
                _root = Nothing
                _nodes.Clear()
                'Purge any nodes we have in our cache and force a refresh from the db
                purgeNodesFromCache()
            End SyncLock
        End Sub

        ''' Recursive function that allows us to find all children nodes of a node of interest
        Private Sub IterateThroughChildrenNodes(ByVal parentNode As SiteMapNode)
            SyncLock _lock
                Dim rootRows() As DataRow = dt.Select("[Parent] = " + parentNode.Key)
                For Each dr As DataRow In rootRows
                    Dim node As SiteMapNode = CreateSiteMapNodeFromDataRow(dr)
                    AddNode(node, GetParentNodeFromDataRow(dr))
                    IterateThroughChildrenNodes(node)
                Next
            End SyncLock
        End Sub
        ''' Returns a datatable detailing all published sections
        Protected Overridable Function getSiteMapTable() As DataTable
            'Return SiteMapIO.bll.Sections.GetListForSiteMap(_name, Resources.MenuResource.var_codlang)
            Return SiteMapBLL.GetListForSiteMap(_name, "")
        End Function
        ''' Returns the entire key of that identifies the sitemap held in our cache
        Public Function getCacheKey() As String
            Return _name & Resources.MainResources.var_codlang & " = getCacheKey"
        End Function

        ''' Builds the sitemap structure
        Public Overloads Overrides Function BuildSiteMap() As SiteMapNode

            ' Return immediately if the sitemap is in the cache
            If HttpContext.Current.Cache(getCacheKey()) IsNot Nothing Then
                Dim mySn As SiteMapNode = HttpContext.Current.Cache(getCacheKey())
                Return mySn
            End If

            'Lock this part to prevent race conditions
            SyncLock _lock

                dt = getSiteMapTable()
                _indexID = dt.Columns("ID").Ordinal()
                _indexUrl = dt.Columns("Url").Ordinal()
                _indexTitle = dt.Columns("Title").Ordinal()
                _indexDesc = dt.Columns("Title").Ordinal
                'Not using roles
                '_indexRoles = dt.Columns("Roles").Ordinal
                _indexParent = dt.Columns("Parent").Ordinal

                Dim rootRow0() As DataRow = dt.Select("ID = 0")
                _root = CreateSiteMapNodeFromDataRow(rootRow0(0))
                AddNode(_root, Nothing)
                'Currently menu items are ordered as they are ordered in the DB.
                Dim underRootRows As DataRow() = dt.Select("[Parent] = 0")

                'We have just grabbed all first-level links, now lets drill down further levels
                If underRootRows.Length > 0 Then
                    For Each dr As DataRow In underRootRows
                        Dim node As SiteMapNode = CreateSiteMapNodeFromDataRow(dr)
                        AddNode(node, GetParentNodeFromDataRow(dr))
                        IterateThroughChildrenNodes(node)
                    Next
                End If
                addNodeToCache(_root)

                Return HttpContext.Current.Cache(getCacheKey())

            End SyncLock
        End Function
        ''' Adds a sitemap node to our Cache
        Protected Sub addNodeToCache(ByVal node As SiteMapNode)
            HttpContext.Current.Cache.Insert(getCacheKey(), node, Nothing, Date.Now.AddMinutes(180), Nothing)
        End Sub
        ''' This method purges sitemap objects stored in the cache after a change is made.
        Protected Overridable Sub purgeNodesFromCache()
            Dim itemsToRemove As List(Of String) = New List(Of String)
            Dim enumerator As IDictionaryEnumerator = HttpContext.Current.Cache.GetEnumerator

            While enumerator.MoveNext()
                If enumerator.Key.ToString().StartsWith(getCacheKey()) Then
                    itemsToRemove.Add(enumerator.Key.ToString())
                End If
            End While

            For Each itemToRemove As String In itemsToRemove
                HttpContext.Current.Cache.Remove(itemToRemove)
            Next
        End Sub
    End Class

End Namespace